package com.tao.filters;

import com.alibaba.fastjson.JSON;
import com.tao.config.RestErrorResponse;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.InputStream;
import java.io.Serializable;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;


/**
 * @author Mr.M
 * @version 1.0
 * @description 网关认证过虑器
 * @date 2022/9/27 12:10
 */
@Component
@Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered {

    // 白名单
    private static List<String> whitelist = new ArrayList<>();

    static {
        // 加载白名单
        try (InputStream resourceAsStream = GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties")) {
            Properties properties = new Properties();
            properties.load(resourceAsStream);
            whitelist.addAll(properties.stringPropertyNames());
        } catch (Exception e) {
            log.error("加载/security-whitelist.properties出错:{}", e.getMessage());
        }
    }

    @Autowired
    private ReactiveJwtDecoder reactiveJwtDecoder;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String requestUrl = exchange.getRequest().getPath().value();
        AntPathMatcher pathMatcher = new AntPathMatcher();

        // 白名单放行
        for (String url : whitelist) {
            if (pathMatcher.match(url, requestUrl)) {
                return chain.filter(exchange);
            }
        }

        // 检查token是否存在
        String token = getToken(exchange);
        if (StringUtils.isBlank(token)) {
            return buildReturnMono("没有认证", exchange);
        }

        // 验证JWT Token
        return reactiveJwtDecoder.decode(token)
                .flatMap(jwt -> {
                            // 解析并存储用户信息到 Exchange 属性中
                            return parseJwtAndStoreAttributes(jwt, exchange, chain);}
                )
                .onErrorResume(ex -> {
                    // 处理JWT解码错误
                    if (ex instanceof BadJwtException) {
                        log.info("认证令牌无效: {}", ex.getMessage());
                        return buildReturnMono("认证令牌无效", exchange);
                    } else if (ex instanceof ExpiredJwtException) {
                        log.info("认证令牌已过期: {}", ex.getMessage());
                        return buildReturnMono("认证令牌已过期", exchange);
                    }
                    return buildReturnMono("认证失败", exchange);
                });
    }

    /**
     * 获取token
     */
    private String getToken(ServerWebExchange exchange) {
        String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (StringUtils.isBlank(tokenStr)) {
            return null;
        }

        // 处理 Bearer token
        if (tokenStr.startsWith("Bearer ")) {
            return tokenStr.substring(7);
        }

        return null;
    }

    private Mono<Void> buildReturnMono(String error, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        RestErrorResponse errorResponse = new RestErrorResponse(error);

        return response
                .writeWith(Mono.just(response.bufferFactory().wrap(
                        JSON.toJSONString(errorResponse).getBytes(StandardCharsets.UTF_8)
                )))
                .doOnNext(aVoid -> {
                    response.setStatusCode(HttpStatus.UNAUTHORIZED);
                    response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                });
    }

    /**
     * 解析 JWT 并存储用户信息
     */
    private Mono<Void> parseJwtAndStoreAttributes(Jwt jwt, ServerWebExchange exchange, GatewayFilterChain chain) {
        try {
            // 直接获取 sub 中的用户 JSON 字符串
            String userJsonStr = jwt.getClaimAsString("sub");

            // 解析用户信息
            User user = JSON.parseObject(userJsonStr, User.class);

            // 将用户信息存储到 Exchange 属性中
            exchange.getAttributes().put("user", user);

            // 获取权限信息
            List<String> authorities = jwt.getClaimAsStringList("authorities");

            // 创建新的 Request，添加用户信息到请求头
            ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
                    .header("x-user-info", URLEncoder.encode(userJsonStr, StandardCharsets.UTF_8.toString()))
                    .header("x-user-id", user.getId())
                    .header("x-username", user.getUsername())
                    .header("x-authorities", String.join(",", authorities != null ? authorities : Collections.emptyList()))
                    .build();

            // 创建新的 Exchange
            ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();

            // 继续过滤器链
            return chain.filter(mutatedExchange);
        } catch (Exception e) {
            log.error("解析用户信息失败", e);
            return buildReturnMono("用户信息解析失败", exchange);
        }
    }
    @Override
    public int getOrder() {
        return -1; // 调整优先级，确保在其他过滤器之前执行
    }

    @Data
    public static class User implements Serializable {

        private static final long serialVersionUID = 1L;

        private String id;

        private String username;

        private String password;

        private String salt;

        private String name;
        private String nickname;
        private String wxUnionid;
        private String companyId;
        /**
         * 头像
         */
        private String userpic;

        private String utype;

        private LocalDateTime birthday;

        private String sex;

        private String email;

        private String cellphone;

        private String qq;

        /**
         * 用户状态
         */
        private String status;

        private LocalDateTime createTime;

        private LocalDateTime updateTime;


    }
}
